﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using ClassifierLib.Classifier;
using ClassifierLib;
using System.ComponentModel;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
using System.Collections;
using ClassifierLib.Classifier.Filters;

using System.Windows.Controls.DataVisualization.Charting;

namespace ClassifierUI
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class Window1 : Window, INotifyPropertyChanged
    {
        #region Implementation of INotifyPropertyChanged

        public event PropertyChangedEventHandler PropertyChanged = null;

        protected void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }

        #endregion 

        private LCS _lcs = new AsyncronousLCS();
        private ClassifierLib.TrainingSet _trainingSet = new MultiplexerTrainingSet(100000, 11);  //TODO: This needs to be moved to LCS.cs
        private StatisticsProvider _statistics;

        /// <summary>
        /// The current LCS
        /// </summary>
        public LCS LCS {
            get
            {
                return _lcs as LCS;
            }
            set
            {
                _lcs = value;
                OnPropertyChanged("LCS");
            }
        }

        /// <summary>
        /// The current training set
        /// </summary>
        public ClassifierLib.TrainingSet TrainingSet
        {
            get
            {
            	return _trainingSet;
            }
        }

        /// <summary>
        /// The current statistics provider
        /// </summary>
        public StatisticsProvider Statistics
        {
            get
            {
                return _statistics;
            }
            set
            {
                _statistics = value;
                OnPropertyChanged("Statistics");
            }
        }

        public Window1()
        {
            InitializeComponent();
        }

        private void WindowLoaded(object sender, RoutedEventArgs e)
        {
            // create statistics class
            Statistics = new StatisticsProvider(LCS,30);
            Statistics.Chart = Chart;
            
            //TODO: Here is an ideal location to modify the filters in the LCS before everything gets set up
            LCS.AddDefaultFilters();
            LCS.Filters.Add(new ExampleStudentProject.FunkyFilter());

             // programmatically populate treeview with filter nodes for now
             // should really be databound
            AddRulesToTreeView();

            //generate columns based on statistics keys used
            AddColumnsToListView();

            LCS.TrainingStarted += LCSTrainingStarted;
            LCS.TrainingEnded += LCSTrainingEnded;   
        }

        private delegate void ParameterlessDelegate();

        /// <summary>
        /// This function is called by the lcs when a training set starts
        /// Call is made from the thread context of the LCS
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void LCSTrainingStarted(object sender, EventArgs e)
        {
            ParameterlessDelegate d = delegate {
                runLCS.IsEnabled = false;
                abortLCS.IsEnabled = true;
            };
            Dispatcher.BeginInvoke(d, null);  
        }

        /// <summary>
        /// This function is called by the lcs when a training set completes
        /// Call is made from the thread context of the LCS
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void LCSTrainingEnded(object sender, EventArgs e)
        {
            ParameterlessDelegate d = delegate
            {
                runLCS.IsEnabled = true;
                abortLCS.IsEnabled = false;
            };
            Dispatcher.BeginInvoke(d, null);  
        }

        private void runLCS_Click(object sender, RoutedEventArgs e)
        {
            LCS.Train(TrainingSet);
        }

        /// <summary>
        /// Aborts a currently running LCS
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void abortLCS_Click(object sender, RoutedEventArgs e)
        {
            (LCS as AsyncronousLCS).Abort();
            runLCS.IsEnabled = true;
            abortLCS.IsEnabled = false;
        }

        /// <summary>
        /// Saves the LCS, ruleset, filters, etc
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void saveLCS_Click(object sender, RoutedEventArgs e)
        {
            Microsoft.Win32.SaveFileDialog sfd = new Microsoft.Win32.SaveFileDialog();
            sfd.Filter = "Xml files|*.xml";

            if (sfd.ShowDialog().Value != true)
            {
                return;
            }

            try
            {
                // Serialization
                var knownTypes = RuleFilter.RegisteredTypes.ToArray();
                XmlSerializer s = new XmlSerializer(typeof(AsyncronousLCS), knownTypes);
                TextWriter w = new StreamWriter(sfd.FileName);
                s.Serialize(w, (AsyncronousLCS)LCS);
                w.Close();
            }
            catch (Exception ex)
            {
                MessageBox.Show("Error saving file: " + ex.ToString());
            }
        }

        /// <summary>
        /// Loads the LCS, ruleset, filters etc
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void loadLCS_Click(object sender, RoutedEventArgs e)
        {
            Microsoft.Win32.OpenFileDialog ofd = new Microsoft.Win32.OpenFileDialog();
            ofd.Filter = "Xml files|*.xml";

            if (ofd.ShowDialog().Value != true)
            {
                return;
            }

            try
            {
                // Deserialization
                var knownTypes = RuleFilter.RegisteredTypes.ToArray();

                XmlSerializer s = new XmlSerializer(typeof(AsyncronousLCS), knownTypes);
                TextReader r = new StreamReader(ofd.FileName);
                LCS = (AsyncronousLCS)s.Deserialize(r);
                r.Close();

                // update ui
                AddRulesToTreeView();
                AddColumnsToListView();
            }
            catch (Exception ex)
            {
                MessageBox.Show("Error loading file: " + ex.ToString());
            }
        }

        /// <summary>
        /// Just saves the rules, not the LCS configuration
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void saveRules_Click(object sender, RoutedEventArgs e)
        {
            Microsoft.Win32.SaveFileDialog sfd = new Microsoft.Win32.SaveFileDialog();
            sfd.Filter = "Xml files|*.xml";

            if (sfd.ShowDialog().Value != true)
            {
                return;
            }

            try
            {
                // Serialization
                XmlSerializer s = new XmlSerializer(typeof(Population));
                TextWriter w = new StreamWriter(sfd.FileName);
                s.Serialize(w, LCS.Rules);
                w.Close();
            }
            catch (Exception ex)
            {
                MessageBox.Show("Error saving file: " + ex.ToString());
            }   
        }

        /// <summary>
        /// Just loads the rules, not the LCS configuration
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void loadRules_Click(object sender, RoutedEventArgs e)
        {

            Microsoft.Win32.OpenFileDialog ofd = new Microsoft.Win32.OpenFileDialog();
            ofd.Filter = "Xml files|*.xml";

            if (ofd.ShowDialog().Value != true)
            {
                return;
            }

            try
            {
                // Deserialization
                XmlSerializer s = new XmlSerializer(typeof(Population));
                TextReader r = new StreamReader(ofd.FileName);
                LCS.Rules = (Population)s.Deserialize(r);
                r.Close();

                // update ui
                AddRulesToTreeView();
                AddColumnsToListView();
            }
            catch (Exception ex)
            {
                MessageBox.Show("Error loading file: " + ex.ToString());
            }
        }

        /// <summary>
        /// Programmatically populates tree view with rules entries
        /// </summary>
        private void AddRulesToTreeView()
        {
            FiltersTreeViewItem.Items.Clear();
            foreach (RuleFilter rule in LCS.Filters)
            {
                TreeViewItem newItem = new TreeViewItem();
                newItem.Header = rule.Name;
                newItem.Tag = rule;
                FiltersTreeViewItem.Items.Add(newItem);
            }
        }
        /// <summary>
        /// Programmatically adds columns to listview based on Rule.InitialStatistics
        /// </summary>
        private void AddColumnsToListView()
        {
            var gridview = listview.View as GridView;
            gridview.Columns.Clear();

            #region Fixed column insertion
            GridViewColumn ConditionColumn = new GridViewColumn();
            GridViewColumnHeader ConditionHeader = new GridViewColumnHeader();
            ConditionHeader.Tag = "Condition";
            ConditionHeader.Content = "Condition";
            ConditionHeader.Click += new RoutedEventHandler(SortClick);
            ConditionColumn.Header = ConditionHeader;
            ConditionColumn.Width = 180;
            ConditionColumn.DisplayMemberBinding = new Binding("Condition");
            GridViewColumn ActionColumn = new GridViewColumn();
            GridViewColumnHeader ActionHeader = new GridViewColumnHeader();
            ActionHeader.Tag = "Action";
            ActionHeader.Content = "Action";
            ActionHeader.Click += new RoutedEventHandler(SortClick);
            ActionColumn.Header = ActionHeader;
            ActionColumn.Width = 100;
            ActionColumn.DisplayMemberBinding = new Binding("Action");
            GridViewColumn NumerosityColumn = new GridViewColumn();
            GridViewColumnHeader NumerosityHeader = new GridViewColumnHeader();
            NumerosityHeader.Tag = "Numerosity";
            NumerosityHeader.Content = "Numerosity";
            NumerosityHeader.Click += new RoutedEventHandler(SortClick);
            NumerosityColumn.Header = NumerosityHeader;
            NumerosityColumn.Width = 100;
            NumerosityColumn.DisplayMemberBinding = new Binding("Numerosity");

            gridview.Columns.Add(ConditionColumn);
            gridview.Columns.Add(ActionColumn);
            gridview.Columns.Add(NumerosityColumn);
            #endregion
            // now programmatically add columns for all statistics
            lock (LCS.Rules)
            {
                foreach (KeyValuePair<string, double> pair in Rule.InitialStatistics)
                {
                    GridViewColumn column = new GridViewColumn();
                    GridViewColumnHeader header = new GridViewColumnHeader();
                    String bindingPath = "Statistics." + pair.Key;
                    header.Content = pair.Key;
                    header.Tag = bindingPath; // used in sorting to find variable name
                    header.Click += new RoutedEventHandler(SortClick);
                    
                    column.Header = header;
                    column.Width = 100;
                    
                    // the following binding works because Rule has fake properties to match entries in Statistics
                    var bind = new Binding();
                    bind.Path = new PropertyPath(bindingPath);
                    column.DisplayMemberBinding = bind;
                    
                    gridview.Columns.Add(column);
                }
            }
        }

        /// <summary>
        /// Refreshes the listview items from the LCS
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Refresh_Click(object sender, System.Windows.RoutedEventArgs e)
        {
			lock(LCS.Rules)
			{
                listview.Items.Clear();
                foreach (Rule row in LCS.Rules)
                { 
                    listview.Items.Add(row);
                }
			}
        }

        private void test_Click(object sender, System.Windows.RoutedEventArgs e)
        {
            //var list = new List<Point>();
            //for (int i = 0; i < 100; i++)
            //{
            //    list.Add(new Point(i, i * 10));
            //}

            //System.Windows.Controls.DataVisualization.Charting.LineSeries lineseries = new System.Windows.Controls.DataVisualization.Charting.LineSeries();
            //Chart.Series.Add(lineseries);
            //lineseries.ItemsSource = list;
            
            //lineseries.IndependentValuePath = "X";
            //lineseries.DependentValuePath = "Y";
            //lineseries.Title = "blah";

            

            
        }

        private GridViewColumnHeader _CurSortCol = null;
        private SortAdorner _CurAdorner = null;
        /// <summary>
        /// Sorts a column in the list view
        /// For further details on how sorting works, see:
        /// http://www.switchonthecode.com/tutorials/wpf-tutorial-using-the-listview-part-2-sorting
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void SortClick(object sender, RoutedEventArgs e)
        {
            GridViewColumnHeader column = sender as GridViewColumnHeader;
            String field = column.Tag as String;

            if (_CurSortCol != null)
            {
                AdornerLayer.GetAdornerLayer(_CurSortCol).Remove(_CurAdorner);
                listview.Items.SortDescriptions.Clear();
            }

            ListSortDirection newDir = ListSortDirection.Ascending;
            if (_CurSortCol == column && _CurAdorner.Direction == newDir)
                newDir = ListSortDirection.Descending;

            _CurSortCol = column;
            _CurAdorner = new SortAdorner(_CurSortCol, newDir);
            AdornerLayer.GetAdornerLayer(_CurSortCol).Add(_CurAdorner);

            try
            {
                listview.Items.SortDescriptions.Add(new SortDescription(field, newDir));
            }
            catch (InvalidOperationException)
            {
                MessageBox.Show("Cannot sort this column, as types are not IComparable");
            }
        }

    }
}
